package model;

import java.util.Random;
import java.util.Vector;

// A specialised Agent : an Ant
public class Ant extends Agent{

	//History of the movements
	Vector<Point> histMoves;
	
	String previousAction;
	
	// The resource carried by the ant
	Resource resource;
	
	// Whether or not the ants put pheromones on the ground
	boolean pheromoneActivated;
	
	// Attraction field
	String attractionField;
	
	//Resources that the ant meet
	int r1Found=0;
	int r2Found=0;
	int r3Found=0;
	
	//Coefficients
	double orientationCoef;
	double pheromoneCoef;
	
	Random rand = new Random();
	
	public Ant(Space2D universe_, Point location, double orientationCoef, double pheromoneCoef, String attractionField) throws Exception {
		super(universe_, location);

		this.orientationCoef = orientationCoef;
		this.pheromoneCoef = pheromoneCoef;
		this.attractionField = attractionField;
		
		this.pheromoneActivated = false;
		this.previousAction = null;
		
		Cell cell = this.universe.getCell(location);
		
		if (cell != null && cell.containsAnt()){
			System.out.println("An ant is already in this location : "+ cell.getAnt().toString() +" "+location.toString());
		} else {
			// Create the name
			this.name = "Ant_"+universe.getCptAnts();
			// Inc cptAnts for future ants
			universe.incCptAnts();
			
			// Creation of the history of moves
			histMoves = new Vector<Point>();
			Point origin = new Point(this.location.getX(), this.location.getY());
			histMoves.add(origin);
			
			// No resource is carried initially
			resource = null;
			
			//Add the ant into the cellsMap
			if (cell == null){
				cell = new Cell();
				universe.setCell(cell, location);
			}
			cell.setAnt(this);
			if (this.pheromoneActivated){
				cell.incPheromone();
				if (!this.universe.listCellsWithPheromone.contains(cell)){
					this.universe.listCellsWithPheromone.add(cell);
				}
			}
			if (cell.containsResourceR1()){
				this.r1Found++;
			}
			if (cell.containsResourceR2()){
				this.r2Found++;
			}
			if (cell.containsResourceR3()){
				this.r3Found++;
			}
			
			System.out.println("Add the agent "+this.getName()+" at the location : "+location.toString());
		}		
	}

	public int getR1Found() {
		return r1Found;
	}

	public void setR1Found(int r1Found) {
		this.r1Found = r1Found;
	}

	public int getR2Found() {
		return r2Found;
	}

	public void setR2Found(int r2Found) {
		this.r2Found = r2Found;
	}

	public int getR3Found() {
		return r3Found;
	}

	public void setR3Found(int r3Found) {
		this.r3Found = r3Found;
	}

	public double getOrientationCoef() {
		return orientationCoef;
	}

	public void setOrientationCoef(double orientationCoef) {
		this.orientationCoef = orientationCoef;
	}

	public double getPheromoneCoef() {
		return pheromoneCoef;
	}

	public void setPheromoneCoef(double pheromoneCoef) {
		this.pheromoneCoef = pheromoneCoef;
	}
	
	public Resource getResource() {
		return resource;
	}

	public void setResource(Resource resource) {
		this.resource = resource;
	}

	public boolean isPheromoneActivated() {
		return pheromoneActivated;
	}

	public void setPheromoneActivated(boolean pheromoneActivated) {
		this.pheromoneActivated = pheromoneActivated;
	}

	public String getAttractionField() {
		return attractionField;
	}

	public void setAttractionField(String attractionField) {
		if (attractionField.equalsIgnoreCase("None")){
			this.attractionField = null;
		} else {
			this.attractionField = attractionField;
		}
	}
	
	public void step() throws Exception{
		Cell cell = this.universe.getCell(location);
		if (cell.containsResource()){
			this.takeResource();
		} else {
			if (this.getResource() != null){
				this.putDownResource();
			} else {
				this.move();
				this.previousAction = "Move";
			}
		}
	}
	
	// Move the agent
	public void move() throws Exception{
		
		Vector<PointWithCoefficient> possibleMoves = this.possibleMoves();
		
		Point dest = null;
		
		dest = randomMovement(possibleMoves);

		changeLocation(this.location, dest);
		Cell cell = this.universe.getCell(dest);
		if (cell != null){
		if (cell.containsResourceR1()){
				this.r1Found++;
			}
			if (cell.containsResourceR2()){
				this.r2Found++;
			}
			if (cell.containsResourceR3()){
				this.r3Found++;
			}
		}
		
	}
	
	// Possible moves that the ant can do
	// The ant cannot go in a cell where there is already another ant
	public Vector<PointWithCoefficient> possibleMoves(){
		Vector<PointWithCoefficient> possibleMoves = new Vector<PointWithCoefficient>();
		for (int i = this.location.getX()-1; i <= this.location.getX()+1; i++){
			if ((i>=0)&&(i<this.universe.getDimX())){
				for (int j = this.location.getY()-1; j <= this.location.getY()+1; j++){
					if ((j>=0)&&(j<this.universe.getDimY())){
						PointWithCoefficient p = new PointWithCoefficient(i,j,0);
						
						//Test if an ant is already in this place
						Cell cell = this.universe.getCell(p);
						
						if ((cell == null)||(!cell.containsAnt())){
							p.addCoef(1);
							//Pheromones
							p.addCoef(cell.pheromone*this.pheromoneCoef);
							//AttractionField
							if (this.attractionField != null){
								if (this.attractionField.equalsIgnoreCase("North")){
									if (i<this.location.getX()){
										p.addCoef(this.orientationCoef);
									}
								}
								if (this.attractionField.equalsIgnoreCase("South")){
									if (i>this.location.getX()){
										p.addCoef(this.orientationCoef);
									}
								}
								if (this.attractionField.equalsIgnoreCase("West")){
									if (j<this.location.getY()){
										p.addCoef(this.orientationCoef);
									}
								}
								if (this.attractionField.equalsIgnoreCase("East")){
									if (j>this.location.getY()){
										p.addCoef(this.orientationCoef);
									}
								}
							}
							possibleMoves.add(p);
						}
					}
				}
			}
		}
		return possibleMoves;
	}
	
	// Random movement of the ant among the possible moves it can do
	// The ant can stay where it is or move to one of the eight cases arount it
	public Point randomMovement(Vector<PointWithCoefficient> possibleMoves){
		Point p = null;
		double sum = 0;
		for (int i=0; i<possibleMoves.size();i++){
			sum += possibleMoves.get(i).getCoef();
		}

		//Random rand = new Random();
		double test = rand.nextDouble();
		
		test = test*sum;
		
		sum = 0;
		int i = 0;
		boolean stop = false;
		while ((!stop)&&(i<possibleMoves.size())){
			sum += possibleMoves.get(i).getCoef();
			if (test<=sum){
				stop = true;
				p = possibleMoves.get(i);
			}
			i++;
		}
		return p;
	}
	
	// Take a resource
	// This method should not be called if the ant is already carrying a resource
	// or if there is no resource where the ant is
	public void takeResource() throws Exception{
		if (this.getResource() != null){
			this.move();
			this.previousAction = "Move";
		} else {
			Cell cell = this.universe.getCell(this.location);
			if (!cell.containsResource()){
				this.move();
				this.previousAction = "Move";
			} else {
				
				boolean willTake = false;
				
				String type = cell.getResource().getType();
				double f = 1 - this.resourcesDetector(type);
				
				if (f == 1.0){
					willTake = true;
				} else {
				
					//Random rand = new Random();
					double test = rand.nextDouble();

					if (f > test){
						willTake = true;
					}
				}
				
				// The ant take the resource
				if (willTake){
					Resource r = cell.getResource();
					cell.removeResource();
					this.setResource(r);
					this.previousAction = "Take";
				} else {
					this.move();
					this.previousAction = "Move";
				}
			}
		}
	}
	
	// Put down the resource the ant is carrying
	// This method should not be called if the ant isn't carrying a resource
	// or if there is a resource where the ant is
	public void putDownResource() throws Exception{
		if (this.getResource() == null){
			this.move();
			this.previousAction = "Move";
		} else {
			Cell cell = this.universe.getCell(this.location);
			if (cell.containsResource()){
				this.move();
				this.previousAction = "Move";
			} else {
				
				boolean willPutDown = false;
				
				String type = getResource().getType();
				double f = this.resourcesDetector(type);
				
				if (f == 1.0){
					willPutDown = true;
				} else {
				
					//Random rand = new Random();
					double test = rand.nextDouble();

					if (f > test){
						willPutDown = true;
					}
				}
				
				// The ant put down the resource
				if (willPutDown){
					cell.setResource(this.getResource());
					this.setResource(null);
					this.previousAction = "PutDown";
				} else {
					this.move();
					this.previousAction = "Move";
				}
			}
		}
	}
	
	//TODO
	// Update the location of the ant in Ant and universe
	public void changeLocation(Point origin, Point dest) throws Exception{
		//Update Ant
		this.location = dest;
		if (this.histMoves.size()>1500){
			Vector<Point> newHistMoves = new Vector<Point>();
			for (int i=500; i<this.histMoves.size();i++){
				newHistMoves.add(this.histMoves.get(i));
			}
			this.histMoves = newHistMoves;
		}
		histMoves.add(dest);
		
		//Update universe
		Cell originCell = this.universe.getCell(origin);
		Cell destCell = this.universe.getCell(dest);
		if (destCell == null){
			destCell = new Cell();
			destCell.setAnt(this);
			if (this.pheromoneActivated){
				destCell.incPheromone();
				if (!this.universe.listCellsWithPheromone.contains(destCell)){
					this.universe.listCellsWithPheromone.add(destCell);
				}
			}
			this.universe.setCell(destCell, dest);
			originCell.removeAnt();
		} else {
			if (destCell.containsAnt()){
				throw new Exception("There is already an ant in this place");
			}else {
				destCell.setAnt(this);
				if (this.pheromoneActivated){
					destCell.incPheromone();
					if (!this.universe.listCellsWithPheromone.contains(destCell)){
						this.universe.listCellsWithPheromone.add(destCell);
					}
				}
				originCell.removeAnt();
			}
		}
	}
	
	public String histMovesString(){
		String ret = "Agent : " + this.getName()+"\n";
		for (int i = 0; i<this.histMoves.size();i++){
			ret += "->"+((Point)histMoves.get(i)).toString()+"\n";
		}
		return ret;
	}
	
	public double resourcesDetector (String type){
		int nb = 0;
		
		Vector<Cell> v = this.universe.getCellsAround(this.location);
		double size = v.size();
		for (int i = 0; i < size; i++){
			Cell cell = v.get(i);
			if (cell != null) {
				Resource r = cell.getResource();
				if (r != null){
					if (r.getType().equalsIgnoreCase(type)){
						nb ++;
					}
				}
			}
		}
		
		double ret = nb / size;
		
		return ret;
	}
	
	// test methods
	public static void testHistMoves(int nbStep, int x, int y) throws Exception{
		Space2D space = new Space2D(50,50);
		Point p = new Point(x,y);
		Ant ant = new Ant(space,p, 0, 0, "None");
		for (int i = 0; i<nbStep; i++){
			ant.move();
		}
		System.out.println(ant.histMovesString());
	}
	
	/*
	public static void testAddAnts() throws Exception{
		Space2D space = new Space2D(50,50);
		Point p1 = new Point(0,0);
		Point p2 = new Point(11,10);
		Point p3 = new Point(50,50);
		Ant a1 = new Ant(space,p1, 0, 0);
		Ant a2 = new Ant(space,p1, 0, 0);
		Ant a3 = new Ant(space,p2, 0, 0);
		Ant a4 = new Ant(space,p2, 0, 0);
		Ant a5 = new Ant(space,p1, 0, 0);
	}
	*/
	
	public static void main(String args[]) throws Exception{
		testHistMoves(1000, 0, 0);
		//testAddAnts();
	}

	public Vector<Point> getHistMoves() {
		return histMoves;
	}
	
}
